﻿//****************************************************************************//
//
// @File: ATSDriver.cs
// @Owner: lyudmilf
// @Test: 
//
// Purpose:
//
//      Entry point for Authentication troubleshooter tool
//
// Notes:
//      
//      Contains AuthenticationTroubleshooterRun and MonitorRun methods - 
//      main entry points for further analyze
//      
//
//****************************************************************************//

using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using SSAT.Utils;

namespace SSAT.ATSDriver
{
    /// <summary>
    /// Represents methods for retrieving info about
    /// authentification fault at the SQL Server.
    /// </summary>
    internal static class AtsDriverFacade
    {
        /// <summary>
        /// Retrieves info about authentification trouble in the 
        /// specified SQL Server DB.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"></exception>
        public static Report AuthenticationTroubleshooterRun(XEventSessionParams xEventSessionParams)
        {
            Verification.CheckArgumentForNull(xEventSessionParams, "xEventSessionParams");

            SqlConnection serverConnection = xEventSessionParams.ServerConnection;
            string xFileName = xEventSessionParams.XFileName;
            string exePath = xEventSessionParams.ExePath;
            int nLastTmpStmp = xEventSessionParams.LastTmpStamp;

            SqlDataReader serverReader = null;

            ReportDetails msReport = new ReportDetails();
            ReportDetails atsReport = new ReportDetails();
            
            try
            {
                Console.WriteLine(ATSDriverResources.RetrievingData);
                SqlCommand serverWait = new SqlCommand();
                serverWait.CommandText = string.Format(Queries.Wait, exePath, xFileName);
                serverWait.Connection = serverConnection;
                serverWait.CommandTimeout = 90;
                serverWait.ExecuteNonQuery();
                SqlCommand serverAnalyse = new SqlCommand();
                serverAnalyse.CommandText = string.Format(Queries.Analyse, exePath, xFileName, nLastTmpStmp);
                serverAnalyse.CommandTimeout = 90;
                serverAnalyse.Connection = serverConnection;

                serverReader = serverAnalyse.ExecuteReader();
                DataTable resultTable = new DataTable();

                if (!serverReader.HasRows)
                {
                    Debug.Assert(false, "Shouldn't get here. If there is nothing to read, we shoudl timeout.");
                    Console.WriteLine(ATSDriverResources.NoAuthentificationErrorsFound);
                }
                else
                {
                    Console.WriteLine(ATSDriverResources.Analyzing);
                    resultTable.Load(serverReader);
                }

                serverReader.Close();

                foreach (DataRow dr in resultTable.Rows)
                {
                    AnalyseErrors.FindErrorAndAnalyse(dr, serverConnection, atsReport);
                }
                msReport = AnalyseErrors.SendMsReport(resultTable, atsReport);
            }
            catch (SqlException se)
            {
                Logging.Handle(se);

                NotifyAboutNotSufficientPermissions();
            }
            catch (Exception e)
            {
                Logging.Handle(e);
                NotifyAboutUnexpectedErrorsAndTryAgain();
            }
            finally
            {
                // Cleanup
                Cleanup(xFileName, serverConnection);
            }
            return new Report(atsReport, msReport);
        }

        private static void Cleanup(string xFileName, SqlConnection serverConnection)
        {
            try
            {
                PostCleanup(serverConnection, xFileName);
                //ServerConnection.Close();
            }
            catch(Exception e)
            {
                Logging.Handle(e);
                NotifyConnectionLostAndSeeErrorLog();
            }
        }

        public static XEventSessionParams CreateXEventSession(SqlConnection serverConnection)
        {
            Console.WriteLine(ATSDriverResources.PreparingXEvent, serverConnection.DataSource);
            string xFileName = string.Empty;
            string exePath = SetupXEventSession(serverConnection, ref xFileName);

            // Get last timestamp from security ring buffer
            SqlCommand serverLastTmpStmp = new SqlCommand(Queries.LastRbEntry, serverConnection);
            SqlDataReader serverReaderLts = serverLastTmpStmp.ExecuteReader();

            int nLastTmpStmp = 0;
            if (serverReaderLts.Read()) //there are records in ring buffer
            {
                nLastTmpStmp = serverReaderLts.IsDBNull(0) ? 0 : serverReaderLts.GetInt32(0);
            }

            serverReaderLts.Close();

            Console.WriteLine(ATSDriverResources.XEventSessionReady);

            XEventSessionParams result = new XEventSessionParams();
            result.XFileName = xFileName;
            result.ExePath = exePath;
            result.LastTmpStamp = nLastTmpStmp;
            result.ServerConnection = serverConnection;
            return result;
        }

        private static void PostCleanup(SqlConnection serverConnection, string xFileName)
        {
            Console.WriteLine(ATSDriverResources.ClosingXEventSession);
            SqlCommand serverPostCleanUp = new SqlCommand(string.Format(Queries.PostCleanup, xFileName), serverConnection);
            serverPostCleanUp.ExecuteNonQuery();
            Console.WriteLine(ATSDriverResources.XEventSessionClosed);
        }

        public static void PostCleanup(SqlConnection serverConnection, XEventSessionParams args)
        {
            Verification.CheckArgumentForNull(serverConnection, "serverConnection");
            if (args != null)
            {
                PostCleanup(serverConnection, args.XFileName);
            }
        }

        private static string SetupXEventSession(SqlConnection serverConnection, ref string xFileName)
        {
            string exePath;
            xFileName = Guid.NewGuid().ToString();
            SqlCommand getPath = new SqlCommand(Queries.FilePath, serverConnection);
            SqlDataReader serverReaderGetPath = getPath.ExecuteReader();
            if (serverReaderGetPath.Read())
            {
                exePath = serverReaderGetPath.GetValue(0).ToString();
                exePath = exePath.Substring(0, exePath.LastIndexOf(Constants.System.PathSeparator));
            }
            else
            {
                throw new Exception(ATSDriverResources.PathNotFound);
            }

            serverReaderGetPath.Close();
                
            SqlCommand serverCleanUp = new SqlCommand(string.Format(Queries.Cleanup, xFileName), serverConnection);
            serverCleanUp.ExecuteNonQuery();

            SqlCommand serverCreateSession = new SqlCommand();
            serverCreateSession.CommandText = string.Format(Queries.CreateSession, exePath, xFileName);
            serverCreateSession.Connection = serverConnection;
            serverCreateSession.ExecuteNonQuery();
            SqlCommand serverStartSession = new SqlCommand(string.Format(Queries.StartSession, xFileName), serverConnection);
            serverStartSession.ExecuteNonQuery();
            return exePath;
        }

        private static void NotifyConnectionLostAndSeeErrorLog()
        {
            AppConsole.WriteInfo(ATSDriverResources.ConnectionLost);
            AppConsole.WriteInfo(ATSDriverResources.SeeErrorLogsForDetails);
        }

        private static void NotifyAboutUnexpectedErrorsAndTryAgain()
        {
            AppConsole.WriteInfo(ATSDriverResources.UnexpectedErrorOccured);
            AppConsole.WriteInfo(ATSDriverResources.TryToRunTheToolAgain);
            AppConsole.WriteInfo(ATSDriverResources.SeeErrorLogsForDetails);
        }

        private static void NotifyAboutNotSufficientPermissions()
        {
            AppConsole.WriteInfo(ATSDriverResources.PossibleReasonsOfSQLException);
            AppConsole.WriteInfo(ATSDriverResources.SeeErrorLogsForDetails);
        }

        public static Report MonitorRun(SqlConnection serverConnection, XEventSessionParams xEventSessionParams)
        {
            Verification.CheckArgumentForNull(serverConnection, "serverConnection");
            Verification.CheckArgumentForNull(xEventSessionParams, "xEventSessionParams");
            ReportDetails atsReport = new ReportDetails();
            string xFileName = xEventSessionParams.XFileName;
            string exePath = xEventSessionParams.ExePath;
            try
            {
                DateTime startDate = DateTime.Now;
                Console.WriteLine(ATSDriverResources.MonitoringStarted);
                Console.WriteLine(ATSDriverResources.RetrievingData);
                SqlCommand serverWait = new SqlCommand();
                serverWait.CommandText = string.Format(Queries.Wait, exePath, xFileName);
                serverWait.Connection = serverConnection;
                serverWait.CommandTimeout = 90;
                serverWait.ExecuteNonQuery();
                DateTime stopDate = DateTime.Now;

                Console.WriteLine(ATSDriverResources.Analyzing);
                    
                TimeSpan timeElapsed = stopDate.Subtract(startDate);
                atsReport.Write(ATSDriverResources.SeparatingLine);
                atsReport.Write(ATSDriverResources.TimeElapsed);
                atsReport.WriteFormat("{0,21}", timeElapsed);

                Analyse.GetStatistics(serverConnection, atsReport, exePath, xFileName);
                atsReport.Write(ATSDriverResources.SeparatingLine);
            }
            catch (SqlException se)
            {
                Logging.Handle(se);
                NotifyAboutNotSufficientPermissions();
            }
            catch (Exception e)
            {
                Logging.Handle(e);
                NotifyAboutUnexpectedErrorsAndTryAgain();
            }
            finally
            {
                Cleanup(xFileName, serverConnection);
            }
            return new Report(atsReport, new ReportDetails());
        }
    }
}
